package com.restaurant.application.util;

import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.nebutown.commonunits.utils.DateTimeUtils;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.TimeZone;
import java.util.stream.Stream;

public class GetDateUtil {
    public static final String FIRST_DAY = "2019-01-01";

    public static Long getFirstDayOfWeek(LocalDate localDate) {
        return localDate.with(DayOfWeek.MONDAY).atStartOfDay().toInstant(ZoneOffset.of("+0")).toEpochMilli();
    }

    public static Long getLastDayOfWeek(LocalDate localDate) {
        return localDate.with(DayOfWeek.SUNDAY).plusDays(1).atStartOfDay().toInstant(ZoneOffset.of("+0")).toEpochMilli() - 1;
    }

    public static Long getLastDayOfWeekNow(LocalDate localDate) {
        long endOfDay = getEndOfDay(LocalDate.now());
        long lastDay = getLastDayOfWeek(localDate);
        return endOfDay < lastDay ? endOfDay : lastDay;
    }

    public static Long getStartOfDay(LocalDate localDate) {
        return localDate.atStartOfDay().toInstant(ZoneOffset.of("+0")).toEpochMilli();
    }

    public static Long getEndOfDay(LocalDate localDate) {
        return localDate.plusDays(1).atStartOfDay().toInstant(ZoneOffset.of("+0")).toEpochMilli() - 1;
    }

    public static Long getFirstDayOfMonth(LocalDate localDate) {
        int year = localDate.getYear();
        int monthValue = localDate.getMonthValue();
        LocalDate date = LocalDate.of(year, monthValue, 1);
        return date.atStartOfDay().toInstant(ZoneOffset.of("+0")).toEpochMilli();
    }

    public static Long getLastDayOfMonth(LocalDate localDate) {
        LocalDate with = localDate.with(TemporalAdjusters.lastDayOfMonth());
        return getEndOfDay(with);
    }

    public static Long getLastDayOfMonthNow() {
        Long endOfDay = getEndOfDay(LocalDate.now());
        Long lastDayOfMonth = getLastDayOfMonth(LocalDate.now());
        return endOfDay < lastDayOfMonth ? endOfDay : lastDayOfMonth;
    }

    public static List<String> getBetweenDate(String start, String end) {
        if (Objects.isNull(end)) {
            end = DateTimeUtils.getCurrentDateStr();
        }
        if (Objects.isNull(start)) {
            start = FIRST_DAY;
        }
        List<String> list = new ArrayList<>();
        LocalDate startDate = LocalDate.parse(start);
        LocalDate endDate = LocalDate.parse(end);

        long distance = ChronoUnit.DAYS.between(startDate, endDate);
        if (distance < 1) {
            if (distance < 0) {
                return list;
            }
            list.add(start);
            return list;
        }
        Stream.iterate(startDate, d -> {
            return d.plusDays(1);
        }).limit(distance + 1).forEach(f -> {
            list.add(f.format(DateTimeFormatter.ISO_DATE));
        });
        return list;
    }

    public static List<String> getBetweenDate(Long start, Long end) {
        if (Objects.isNull(end)) {
            end = DateTimeUtils.getCurrentMillisecond();
        }
        if (Objects.isNull(start)) {
            start = DateTimeUtils.convertTimeToLong(FIRST_DAY);
        }
        String startS = getDateFromTimestamp(start).toString();
        String endS = getDateFromTimestamp(end).toString();
        return getBetweenDate(startS, endS);
    }

    public static LocalDateTime getDateTimeFromTimestamp(long timestamp) {
        if (timestamp == 0)
            return null;
        return LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp), TimeZone
                .getDefault().toZoneId());
    }

    public static LocalDate getDateFromTimestamp(long timestamp) {
        LocalDateTime date = getDateTimeFromTimestamp(timestamp / 1000);
        return date == null ? null : date.toLocalDate();
    }

    public static List<String> getBetweenWeek(String start, String end) {
        if (Strings.isNullOrEmpty(start)) {
            start = FIRST_DAY;
        }
        if (Strings.isNullOrEmpty(end)) {
            end = DateTimeUtils.getCurrentDateStr();
        }
        LocalDate beginDate = parseStrToLocalDate(start);
        LocalDate endDate = parseStrToLocalDate(end);
        if (beginDate.compareTo(endDate) > 0) {
            return Lists.newArrayList();
        }
        LocalDate firstMonday = getDateFromTimestamp(getFirstDayOfWeek(beginDate));
        LocalDate lastSunday = getDateFromTimestamp(getLastDayOfWeek(endDate));
        List<String> weeks = Lists.newArrayList();
        int countWeek = 7;
        while (true) {
            if (lastSunday.compareTo(firstMonday) <= 0) {
                break;
            }
            if (weeks.size() == 0) {
                weeks.add(firstMonday.plusDays(1).toString() + "~" + firstMonday.plusDays(6).toString());
                firstMonday = firstMonday.plusDays(6);
            } else {
                weeks.add(firstMonday.plusDays(1).toString() + "~" + firstMonday.plusDays(countWeek).toString());
                firstMonday = firstMonday.plusDays(countWeek);
            }
        }
        //最后一周处理
        String lastWeek = weeks.get(weeks.size() - 1);
        String lastWeekLastDay = lastWeek.split("~")[1];
        LocalDate lastDay = parseStrToLocalDate(lastWeekLastDay);
        if (lastDay.compareTo(endDate) < 0) {
            weeks.add(lastDay.plusDays(1).toString() + "~" + endDate.toString());
        }
        dealWithLastWeek(weeks, endDate);

        //开始一周处理
        dealWithFirstWeek(weeks, beginDate);
        return weeks;
    }

    public static List<String> getBetweenWeek(Long start, Long end) {
        return getBetweenWeek(getDateFromTimestamp(start).toString(), getDateFromTimestamp(end).toString());
    }

    public static LocalDate parseStrToLocalDate(String str) {
        return LocalDate.parse(str, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    }

    private static void dealWithFirstWeek(List<String> weeks, LocalDate beginDate) {
        String firstWeek = weeks.get(0);
        String firstWeekLastDay = firstWeek.split("~")[1];
        LocalDate firstWeekLastDayDate = parseStrToLocalDate(firstWeekLastDay);
        weeks.remove(0);
        weeks.add(0, beginDate.toString() + "~" + firstWeekLastDayDate.toString());
    }

    private static void dealWithLastWeek(List<String> weeks, LocalDate endDate) {
        String lastWeekStr = weeks.get(weeks.size() - 1);
        String lastWeekEndDay = lastWeekStr.split("~")[0];
        weeks.remove(weeks.size() - 1);
        weeks.add(lastWeekEndDay + "~" + endDate.toString());
    }

    public static List<String> getBetweenMonth(String start, String end) {
        if (Strings.isNullOrEmpty(start)) {
            start = FIRST_DAY;
        }
        if (Strings.isNullOrEmpty(end)) {
            end = DateTimeUtils.getCurrentDateStr();
        }
        ArrayList<String> result = new ArrayList();
        LocalDate startDate = getDateFromTimestamp(getFirstDayOfMonth(parseStrToLocalDate(start)));
        LocalDate endDate = getDateFromTimestamp(getFirstDayOfMonth(parseStrToLocalDate(end)));
        while (startDate.compareTo(endDate) <= 0) {
            result.add(startDate.toString().substring(0, 7));
            startDate = startDate.plusMonths(1);
        }
        return result;
    }

    public static List<String> getBetweenMonth(long start, long end) {
        return getBetweenMonth(getDateFromTimestamp(start).toString(), getDateFromTimestamp(end).toString());
    }

    public static void main(String[] args) {
        Long lastDayOfMonth = getFirstDayOfWeek(LocalDate.of(2019, 07, 26));
        System.out.println(getDateFromTimestamp(lastDayOfMonth).toString());
    }

}
